package com.ishansong.gis.tool;

import com.mongodb.Block;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.UpdateOptions;
import org.bson.BsonDocument;

import java.util.List;

/**
 * Created by danqiang on 2017/7/17.
 * mongoClient操作
 */
class Mongo {
    private MongoClient mongoClient = null;
    private MongoDatabase mongoDatabase = null;
    private MongoCollection<BsonDocument> collection = null;

    Mongo(List<ServerAddress> seeds) {
        //maxWaitQueueSize 等于 threadsAllowedToBlockForConnectionMultiplier * maxConnectionsPerHost
        //threadsAllowedToBlockForConnectionMultiplier的默认值为5
        //maxConnectionsPerHost的默认值为100
        //见MongoClientOptions.class中的如下代码
        //maxWaitQueueSize(this.getThreadsAllowedToBlockForConnectionMultiplier() * this.getConnectionsPerHost())
        MongoClientOptions mongoClientOptions = MongoClientOptions.builder().connectionsPerHost(1000).build();
        mongoClient = new MongoClient(seeds, mongoClientOptions);
    }

    void switchCollection(String dbName, String collectionName) {
        mongoDatabase = mongoClient.getDatabase(dbName);
        if (!collectionName.isEmpty()) {
            collection = mongoDatabase.getCollection(collectionName, BsonDocument.class);
        }
    }

    long count(BsonDocument criteria) {
        return collection.count(criteria);
    }

    void find(int limit, final Block<BsonDocument> cb) {
        collection.find().limit(limit).forEach(cb);
    }

    void find(BsonDocument criteria, final Block<BsonDocument> cb) {
        collection.find(criteria).forEach(cb);
    }

    void iterateWithCollectionName(final Block<BsonDocument> cb, final String collectionName) {
        final int len = collectionName.length();
        mongoDatabase.listCollectionNames().forEach(new Block<String>() {
            public void apply(String s) {
                if (s.length() >= len && s.substring(0, len).equals(collectionName)) {
                    MongoCollection<BsonDocument> collection = mongoDatabase.getCollection(s, BsonDocument.class);
                    collection.find().forEach(cb);
                }
            }
        });
    }

    long countAll(final String collectionName) {
        final long[] count = {0};
        final int len = collectionName.length();
        mongoDatabase.listCollectionNames().forEach(new Block<String>() {
            public void apply(String s) {
                if (s.length() >= len && s.substring(0, len).equals(collectionName)) {
                    MongoCollection<BsonDocument> collection = mongoDatabase.getCollection(s, BsonDocument.class);
                    count[0] += collection.count();
                }
            }
        });

        return count[0];
    }

    private void insert(BsonDocument doc) {
        try {
            collection.insertOne(doc);
        } catch (Exception e) {
            System.out.println("insert failed: " + doc.toJson() + "cause: " + e.getMessage());
            e.printStackTrace();
        }
    }

    void createIndex(BsonDocument index) {
        collection.createIndex(index);
    }

    void customUpsert(BsonDocument criteria, BsonDocument doc) {
        try {
            long result = collection.count(criteria);

            if (result == 0) {
                insert(doc);
            }
        } catch (Exception e) {
            System.out.println("customUpsert failed: criteria = " + criteria.toJson() + ",doc = " + doc.toJson() + "cause: " + e.getMessage());
            e.printStackTrace();
        }
    }

    void upsert(BsonDocument criteria, BsonDocument doc) {
        try {
            collection.updateOne(criteria, new BsonDocument("$set", doc), new UpdateOptions().upsert(true));
        } catch (Exception e1) {
            //mongos 挂掉一个之后会抛异常，重试会使用另一个mongos
            try {
                collection.updateOne(criteria, new BsonDocument("$set", doc), new UpdateOptions().upsert(true));
            } catch (Exception e2) {
                System.out.println("upsert failed: " + doc.toJson() + "cause: " + e2.getMessage());
                e2.printStackTrace();
            }
        }
    }
}
